﻿/*--------------------------------------------------------------
 * Author : Shine K Velayudhan 
 ---------------------------------------------------------------*/
using System;
using System.Collections.Generic;

namespace IQN.Logging
{
    internal sealed class LoggerRepository
    {
        private readonly object cacheCreationLock = new object();
        private MessageCache<ErrorMessage> errorLogCache = null;
        private MessageCache<PerformanceMessage> performanceLogCache = null;
        private MessageCache<TraceMessage> traceLogCache = null;
        private readonly Logger rootNode = null;
        private readonly object treeLock = new object();
        private readonly Dictionary<string, Logger> treeNodes = null;
        private const int DEF_ErrorLogCacheSize = 4;
        private const int DEF_PerformanceLogCacheSize = 512;
        private const int DEF_TraceLogCacheSize = 512;
        private const int DEF_TreeCapacity = 32;
        private const string ROOT_LOGGER_NAME = "root";

        internal LoggerRepository()
        {
            this.rootNode = new Logger(this, "root");
            this.treeNodes = new Dictionary<string, Logger>(DEF_TreeCapacity);
        }

        private Logger FindNearestParentByName(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            int length = name.LastIndexOf('.');
            if (length < 0)
            {
                return null;
            }
            string key = name.Substring(0, length);
            Logger logger = null;
            if (this.treeNodes.TryGetValue(key, out logger) && (null != logger))
            {
                return logger;
            }
            return logger;
        }
        internal Logger GetLogger(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("Logger name required.");
            }
            Logger logger = null;
            lock (this.treeLock)
            {
                if (this.treeNodes.TryGetValue(name, out logger) && (null != logger))
                {
                    return logger;
                }
                Logger item = new Logger(this, name);
                Logger parent = this.FindNearestParentByName(name);
                if (null == parent)
                {
                    parent = this.rootNode;
                }
                if (parent.HasChildren)
                {
                    List<Logger> list = new List<Logger>(parent.Children.Count);
                    foreach (Logger child in parent.Children)
                    {
                        if (child.LoggerName.StartsWith(name))
                        {
                            list.Add(child);
                        }
                    }
                    foreach (Logger child in list)
                    {
                        parent.Children.Remove(child);
                        item.EnsureChildren().Add(child);
                        child.Parent = item;
                    }
                }
                item.Parent = parent;
                parent.EnsureChildren().Add(item);
                this.treeNodes.Add(name, item);
                return item;
            }
        }

        internal bool TryGetLogger(string loggerName, out Logger logger)
        {
            if (string.IsNullOrEmpty(loggerName))
            {
                throw new ArgumentException("loggerName required.");
            }
            return (this.treeNodes.TryGetValue(loggerName, out logger) && (null != logger));
        }

        internal IEnumerable<string> ActiveLoggerNames
        {
            get
            {
                lock (this.treeLock)
                {
                    return new List<string>(this.treeNodes.Keys);
                }
            }
        }

        internal MessageCache<ErrorMessage> ErrorLogCache
        {
            get
            {
                if (null != this.errorLogCache)
                {
                    return this.errorLogCache;
                }
                lock (this.cacheCreationLock)
                {
                    if (null == this.errorLogCache)
                    {
                        this.errorLogCache = new MessageCache<ErrorMessage>(DEF_ErrorLogCacheSize);
                    }
                    return this.errorLogCache;
                }
            }
        }

        internal MessageCache<PerformanceMessage> PerformanceLogCache
        {
            get
            {
                if (null != this.performanceLogCache)
                {
                    return this.performanceLogCache;
                }
                lock (this.cacheCreationLock)
                {
                    if (null == this.performanceLogCache)
                    {
                        this.performanceLogCache = new MessageCache<PerformanceMessage>(DEF_PerformanceLogCacheSize);
                    }
                    return this.performanceLogCache;
                }
            }
        }

        internal Logger RootLogger
        {
            get
            {
                return this.rootNode;
            }
        }

        internal MessageCache<TraceMessage> TraceLogCache
        {
            get
            {
                if (null != this.traceLogCache)
                {
                    return this.traceLogCache;
                }
                lock (this.cacheCreationLock)
                {
                    if (null == this.traceLogCache)
                    {
                        this.traceLogCache = new MessageCache<TraceMessage>(DEF_TraceLogCacheSize);
                    }
                    return this.traceLogCache;
                }
            }
        }
    }
}
